package br.usp.ime.origami.foldcut.geometry;

import java.awt.Shape;
import java.awt.geom.Line2D;
import java.util.Collections;

import br.usp.ime.origami.foldcut.gui.util.Figure;
import br.usp.ime.origami.foldcut.gui.util.OrigamiShape;
import br.usp.ime.origami.foldcut.structures.simplegraph.Edge;
import br.usp.ime.origami.foldcut.structures.simplegraph.SimpleEdge;
import br.usp.ime.origami.model.Point;
import br.usp.ime.origami.model.SimplePoint;
import br.usp.ime.origami.primitives.NumberPrimitives;
import br.usp.ime.origami.view.Drawable;

public class Line implements Drawable
{
	private Point point;

	// angulo em radianos
	private double angle;

	public Line(Point vertex, double angle)
	{
		this.point = vertex;

		this.angle = ((angle % (2 * Math.PI)) + (2 * Math.PI)) % (2 * Math.PI);
		if (this.angle < 0 || this.angle > (2 * Math.PI))
			throw new Error();
	}

	public Line(Point p1, Point p2)
	{
		this(new SimpleEdge(p1, p2));
	}

	public Line(Edge e)
	{
		this.point = e.getStart();

		double dx = e.getEnd().getX() - e.getStart().getX();
		double dy = e.getEnd().getY() - e.getStart().getY();

		if (NumberPrimitives.isZero(dx))
		{
			this.angle = Math.PI / 2;
		}
		else
		{
			this.angle = Math.atan(dy / dx);
			if (dx < 0)
				this.angle += Math.PI;
			this.angle = ((angle % (2 * Math.PI)) + (2 * Math.PI)) % (2 * Math.PI);
			if (this.angle < 0 || this.angle > (2 * Math.PI))
				throw new Error();
		}

	}

	@Override
	public String toString()
	{
		return "Angulo = " + angle + ", Ponto = " + point;
	}

	/**
	 * @return Returns the angle.
	 */
	public double getAngle()
	{
		return angle;
	}

	/**
	 * @return Returns the point.
	 */
	public Point getPoint()
	{
		return point;
	}

	public Figure toFigure()
	{
		Shape line = new Line2D.Double(point.getX() - (angle * 1000), point.getY() - 1000, point.getX()
				+ (angle * 1000), point.getY() + 1000);

		return new Figure(Collections.singletonList(new OrigamiShape(line, OrigamiShape.DrawScheme.LINE)));
	}

	@Override
	public boolean equals(Object obj)
	{
		Line l = (Line) obj;

		if (this.isVertical() && l.isVertical())
		{
			return NumberPrimitives.equals(point.getX(), l.point.getX());
		}

		if (!NumberPrimitives.equals(Math.tan(angle), Math.tan(l.angle)))
		{
			return false;
		}

		double y = Math.tan(angle) * (point.getX() - l.point.getX()) + l.point.getY();

		return NumberPrimitives.equals(point.getY(), y);
	}

	private boolean isVertical()
	{

		return NumberPrimitives.equals(angle, Math.PI / 2) || NumberPrimitives.equals(angle, 3 * Math.PI / 2);
	}

	@Override
	public int hashCode()
	{

		return (int) (point.hashCode() * angle * 1000);
	}

	public Line perpendicularAt(Point p)
	{
		return new Line(p, angle + Math.PI / 2);
	}

	public Point intersectionWith(Line l)
	{

		if (isVertical() && l.isVertical())
		{
			throw new IllegalArgumentException("ambas verticais");
		}
		if (isVertical())
		{
			return new SimplePoint(point.getX(), l.pointAt(point.getX()));
		}
		if (l.isVertical())
		{
			return new SimplePoint(l.getPoint().getX(), pointAt(l.getPoint().getX()));

		}
		double x = l.getPoint().getY() - point.getY() + point.getX() * Math.tan(angle) - l.getPoint().getX()
				* Math.tan(l.getAngle());
		x /= Math.tan(angle) - Math.tan(l.getAngle());
		return new SimplePoint(x, pointAt(x));

	}

	public Line getBissector(Line line)
	{
		if (NumberPrimitives.equals(angle, line.getAngle()))
		{
			throw new IllegalArgumentException();
		}

		double newAngle = ((angle - line.getAngle()) / 2) + line.getAngle();
		return new Line(intersectionWith(line), newAngle);
	}

	public double getLinearCoeficient()
	{
		if (NumberPrimitives.isZero(point.getX()))
		{
			return point.getY();
		}
		return point.getY() - Math.tan(angle) * point.getX();
	}

	public double pointAt(double x)
	{
		if (isVertical())
		{
			if (NumberPrimitives.equals(x, point.getX()))
			{
				return point.getY();
			}
			throw new IllegalArgumentException("reta vertical!");
		}
		return point.getY() + Math.tan(angle) * (x - point.getX());
	}

	public static void main(String[] args)
	{
		System.out.println(((-8 % 5) + 5) % 5);
		System.out.println(new Line(new SimplePoint(0, 0), -4 * Math.PI).getAngle());
	}
}
